package org.apache.mina.common;

import java.io.InvalidObjectException;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

/**
 * Represents network transport types. MINA provides three transport types by
 * default:
 * <ul>
 * <li>{@link #SOCKET} - TCP/IP</li>
 * <li>{@link #DATAGRAM} - UDP/IP</li>
 * <li>{@link #VM_PIPE} - in-VM pipe support (only available in protocol layer</li>
 * </ul>
 * <p>
 * 
 * 代表网络通信协议。MINA提供3种传输协议：TCP/IP；UDP/IP；VM内部管道通讯（仅在协议层起作用）。
 * 
 * You can also create your own transport type. Please refer to
 * {@link #TransportType(String[], boolean)}.
 * 
 * 你也可以创建自己的通信协议。
 */
public final class TransportType implements Serializable {
    private static final long                       serialVersionUID = 3258132470497883447L;

    private static final Map<String, TransportType> name2type        = new HashMap<String, TransportType>();

    /**
     * 添加键值对，其中names数组中的每个字符串，均对应着同一个type。
     * 
     * @param names
     * @param type
     */
    private static void register(String[] names, TransportType type) {
        synchronized (name2type) {
            for (int i = names.length - 1; i >= 0; i--) {
                if (name2type.containsKey(names[i])) {
                    throw new IllegalArgumentException("Transport type name '"
                            + names[i] + "' is already taken.");
                }
            }

            for (int i = names.length - 1; i >= 0; i--) {
                name2type.put(names[i].toUpperCase(), type);
            }
        }
    }

    /**
     * Transport type: TCP/IP (Registry name: <tt>"SOCKET"</tt> or
     * <tt>"TCP"</tt>)
     * 
     * TCP/IP通信协议，注册名为SOCKET或TCP。
     */
    public static final TransportType SOCKET   = new TransportType(
                                                       new String[] { "SOCKET",
            "TCP"                                     }, false);

    /**
     * Transport type: UDP/IP (Registry name: <tt>"DATAGRAM"</tt> or
     * <tt>"UDP"</tt>)
     * 
     * UDP/IP通信协议，注册名为DATAGRAM或UDP。
     */
    public static final TransportType DATAGRAM = new TransportType(
                                                       new String[] {
            "DATAGRAM", "UDP"                         }, true);

    /**
     * Transport type: in-VM pipe (Registry name: <tt>"VM_PIPE"</tt>) Please
     * refer to <a href="../protocol/vmpipe/package-summary.htm">
     * <tt>org.apache.mina.protocol.vmpipe</tt></a> package.
     * 
     * VM内部管道通讯，注册名为VM_PIPE。
     */
    public static final TransportType VM_PIPE  = new TransportType(
                                                       new String[] { "VM_PIPE" },
                                                       Object.class, false);

    /**
     * Returns the transport type of the specified name. All names are
     * case-insensitive.
     * 
     * 根据指定的name，返回通讯协议类型。name大小写无关。
     * 
     * @param name
     *            the name of the transport type
     * @return the transport type
     * @throws IllegalArgumentException
     *             if the specified name is not available.
     */
    public static TransportType getInstance(String name) {
        TransportType type = name2type.get(name.toUpperCase());
        if (type != null) {
            return type;
        }

        throw new IllegalArgumentException("Unknown transport type name: "
                + name);
    }

    private final String[]                          names;

    // 此对象不被序列化
    private final transient boolean                 connectionless;

    // 此对话不被序列化
    private final transient Class<? extends Object> envelopeType;

    /**
     * Creates a new instance. New transport type is automatically registered to
     * internal registry so that you can look it up using
     * {@link #getInstance(String)}.
     * 
     * 创建一个新的实例。新的通讯类型会自动注册到类型注册器，你可以通过getInstance(String)来获得该实例。
     * 
     * @param names
     *            the name or aliases of this transport type
     * @param connectionless
     *            <tt>true</tt> if and only if this transport type is
     *            connectionless
     * 
     * @throws IllegalArgumentException
     *             if <tt>names</tt> are already registered or empty
     */
    public TransportType(String[] names, boolean connectionless) {
        this(names, ByteBuffer.class, connectionless);
    }

    /**
     * Creates a new instance. New transport type is automatically registered to
     * internal registry so that you can look it up using
     * {@link #getInstance(String)}.
     * 
     * @param names
     *            the name or aliases of this transport type
     * @param connectionless
     *            <tt>true</tt> if and only if this transport type is
     *            connectionless
     * 
     * @throws IllegalArgumentException
     *             if <tt>names</tt> are already registered or empty
     */
    public TransportType(String[] names, Class<? extends Object> envelopeType,
            boolean connectionless) {
        if (names == null) {
            throw new NullPointerException("names");
        }
        if (names.length == 0) {
            throw new IllegalArgumentException("names is empty");
        }
        if (envelopeType == null) {
            throw new NullPointerException("envelopeType");
        }

        for (int i = 0; i < names.length; i++) {
            if (names[i] == null) {
                throw new NullPointerException("strVals[" + i + "]");
            }

            names[i] = names[i].toUpperCase();
        }

        register(names, this);
        this.names = names;
        this.connectionless = connectionless;
        this.envelopeType = envelopeType;
    }

    /**
     * Returns <code>true</code> if the session of this transport type is
     * connectionless.
     */
    public boolean isConnectionless() {
        return connectionless;
    }

    public Class<? extends Object> getEnvelopeType() {
        return envelopeType;
    }

    /**
     * Returns the known names of this transport type.
     */
    public Set<String> getNames() {
        Set<String> result = new TreeSet<String>();
        for (int i = names.length - 1; i >= 0; i--) {
            result.add(names[i]);
        }

        return result;
    }

    @Override
    public String toString() {
        return names[0];
    }

    private Object readResolve() throws ObjectStreamException {
        for (int i = names.length - 1; i >= 0; i--) {
            try {
                return getInstance(names[i]);
            } catch (IllegalArgumentException e) {
                // ignore
            }
        }

        throw new InvalidObjectException("Unknown transport type.");
    }
}
